/**
 *******************************************************************************
 * @file rtc_driver.c
 * @author .ISS_AUTO (hudandan@issauto.com)
 * @version xx.xx.xx
 * @date    xx.xx.xx
 * @brief
 *
 * @copyright Copyright (c) 2023 ISSAUTO TECH Co., Ltd. All rights reserved.
 *
 *******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "rtc_driver.h"

/*****************************************************************************
 * Copyright (c) 2019, Nations Technologies Inc.
 *
 * All rights reserved.
 * ****************************************************************************
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the disclaimer below.
 *
 * Nations' name may not be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY NATIONS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * DISCLAIMED. IN NO EVENT SHALL NATIONS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ****************************************************************************/

/**
 * @file User_RTC_Config.c
 * @author Nations
 * @version v1.0.1
 *
 * @copyright Copyright (c) 2019, Nations Technologies Inc. All rights reserved.
 */



#include <stdio.h>
#include <stdint.h>
#include "n32g45x_rtc.h"

/* Exported Function ---------------------------------------------------------*/
/* Exported variables --------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/**
 * @brief delay timeout
 *
 * @param ms
 */
void RTC_Delay_Xms(uint32_t ms)
{
    ms *= 0x1000;

    while (1) {
        if (ms-- == 0) {
            return;
        }
    }
}

/**
 * @brief  Display the current alarm time on the Hyperterminal.
 * @param AlarmX ALARMA or ALARMB
 */
void RTC_AlarmShow(uint8_t AlarmX, RTC_AlarmType *pRTC_Alarm)
{
    /* Get the current Alarm */
    if (AlarmX == 0)
        RTC_GetAlarm(RTC_FORMAT_BIN, RTC_A_ALARM, pRTC_Alarm);
    else
        RTC_GetAlarm(RTC_FORMAT_BIN, RTC_B_ALARM, pRTC_Alarm);

    log_info("\r\n#[>v<] Current Alarm Display:\r\n");
    log_info(" Alarm %s: %0.2d:%0.2d:%0.2d\r\n",
             (AlarmX == 0) ? ("A") : ("B"),
             pRTC_Alarm->AlarmTime.Hours,
             pRTC_Alarm->AlarmTime.Minutes,
             pRTC_Alarm->AlarmTime.Seconds);
}

/**
 * @brief  Display the current Date on the Hyperterminal.
 */
void RTC_DateShow(RTC_DateType *pRTC_Data)
{
    /* Get the current Date */
    RTC_GetDate(RTC_FORMAT_BIN, pRTC_Data);
    log_info("#[>v<] Current Date Display:\r\n");
    log_info(" 20%0.2d-%0.2d-%0.2d [%d]\r\n",
             pRTC_Data->Year,
             pRTC_Data->Month,
             pRTC_Data->Date,
             pRTC_Data->WeekDay
            );
}

/**
 * @brief  Display the current time on the Hyperterminal.
 */
void RTC_TimeShow(RTC_TimeType *pRTC_Time)
{
    /* Get the current Time and Date */
    RTC_GetTime(RTC_FORMAT_BIN, pRTC_Time);
    log_info("#[>v<] Current Time Display:\r\n");
    log_info(" %0.2d:%0.2d:%0.2d\r\n",
             pRTC_Time->Hours,
             pRTC_Time->Minutes,
             pRTC_Time->Seconds
            );

    /* Unfreeze the RTC DAT Register */
    (void)RTC->DATE;
}

/**
 * @brief  Display the current TimeStamp (time and date) on the Hyperterminal.
 */
void RTC_TimeStampShow(void)
{
    RTC_TimeType RTC_TimeStampStructure;
    RTC_DateType RTC_TimeStampDateStructure;

    /* Get the current TimeStamp */
    RTC_GetTimeStamp(RTC_FORMAT_BIN, &RTC_TimeStampStructure, &RTC_TimeStampDateStructure);
    log_info("\r\n#[>v<] TimeStamp Display (Time and Date): \r\n");
    log_info("\r\nThe current timestamp Time is:  %0.2d:%0.2d:%0.2d \r\n",
             RTC_TimeStampStructure.Hours,
             RTC_TimeStampStructure.Minutes,
             RTC_TimeStampStructure.Seconds);
    log_info("\r\nThe current timestamp Date (MM-DD W) is: %0.2d-%0.2d %0.2d \r\n",
             RTC_TimeStampDateStructure.WeekDay,
             RTC_TimeStampDateStructure.Date,
             RTC_TimeStampDateStructure.Month);
}

/**
 * @brief  RTC alarm regulate with the default value.
 *
 * @param RTC_Alarm specifies the alarm to be configured.
 *   This parameter can be any combination of the following values:
 *     @arg RTC_A_ALARM to select Alarm A.
 *     @arg RTC_B_ALARM to select Alarm B.
 */
ErrorStatus RTC_AlarmRegulate(uint32_t RTC_Alarm, RTC_AlarmType *pAlarm)
{
    ISS_ASSERT(pAlarm != NULL);
    /** @note: Alarm Settings range must be:
     *    Hours   ∈ [0, 23]
     *    Minutes ∈ [0, 59]
     *    Seconds ∈ [0, 59]
     */
    ISS_ASSERT((pAlarm->AlarmTime.Hours <= 23)
               && (pAlarm->AlarmTime.Minutes <= 59)
               && (pAlarm->AlarmTime.Seconds <= 59));

    /* Disable the AlarmX */
    RTC_EnableAlarm(RTC_Alarm, DISABLE);

    /* Configure the RTC Alarm X register */
    RTC_SetAlarm(RTC_FORMAT_BIN, RTC_Alarm, pAlarm);

    /* Enable the alarm */
    RTC_EnableAlarm(RTC_Alarm, ENABLE);

    return SUCCESS;
}

/**
 * @brief  RTC date regulate with the default value.
 *
 * @param[in] pDate
 */
ErrorStatus RTC_DateRegulate(RTC_DateType *pDate)
{
    ISS_ASSERT(pDate != NULL);
    /** @note: Date Settings range must be:
     *    WeekDay ∈ [1,  7]
     *    Date    ∈ [1, 31]
     *    Month   ∈ [1, 12]
     *    Year    ∈ [0, 99]
     */
    ISS_ASSERT(((pDate->WeekDay >= 1) && (pDate->WeekDay <= 7))
               && ((pDate->Date >= 1) && (pDate->Date <= 31))
               && ((pDate->Month >= 1) && (pDate->Month <= 12))
               && (pDate->Year <= 99));

    /* Configure the RTC date register */
    if (RTC_SetDate(RTC_FORMAT_BIN, pDate) == ERROR) {
        return ERROR;
    }

    return SUCCESS;
}

/**
 * @brief  RTC time regulate with the default value.
 *
 * @param[in] pTime
 */
ErrorStatus RTC_TimeRegulate(RTC_TimeType *pTime)
{
    ISS_ASSERT(pTime != NULL);
    /** @note: Time Settings range must be:
     *    Hours   ∈ [0, 23]
     *    Minutes ∈ [0, 59]
     *    Seconds ∈ [0, 59]
     */
    ISS_ASSERT((pTime->H12 != 0xFF)
               && (pTime->Hours <= 23)
               && (pTime->Minutes <= 59)
               && (pTime->Seconds <= 59));

    if (RTC_ConfigTime(RTC_FORMAT_BIN, pTime) == ERROR) {
        return ERROR;
    }

    return SUCCESS;
}

/**
 * @brief  Configures the RTC Source Clock Type.
 *
 * @param[in] prtc
 */

/** @note Whether Reset The Backup Area.
 */
#define IS_RST_BKP            (0U)

static ErrorStatus RTC_CLKSourceConfig(RTC_DriverTypeDef *prtc)
{
    uint32_t SynchPrediv, AsynchPrediv;

    ISS_ASSERT(prtc != NULL);

    /* Allow access to RTC */
    PWR_BackupAccessEnable(ENABLE);

    /* Reset Backup */
    #if (IS_RST_BKP)
    BKP_DeInit();
    #endif

    /* Disable RTC clock */
    RCC_EnableRtcClk(DISABLE);

    #if (RTC_CLK_SRC_TYPE_HSE128 == CLK_SRC_TYPE)

    log_info("\r\n#o.0 RTC_ClkSrc: HSE128\r\n");

    /** @note 📌系统需提前初始化RCC，并且配置为HSE 或 HSE+PLL */

    RCC_EnableLsi(DISABLE); //!< LSI is turned off here to ensure that only one clock is turned on

    /* Enable RTC source clock HSE_DIV128 */
    RCC_ConfigRtcClk(RCC_RTCCLK_SRC_HSE_DIV128);

    SynchPrediv  = 0x1E8; //!< 8M/128 = 62.5KHz
    AsynchPrediv = 0x7F;  //!< value range: 0-7F

    #elif (RTC_CLK_SRC_TYPE_LSE == CLK_SRC_TYPE)

#define RTC_LSE_TRY_COUNT         250

    uint8_t lse_ready_count = 0;

    log_info("\r\n#o.0 RTC_ClkSrc: LSE\r\n");

    /** @note 📌系统当前未配置LSE时，需先配置LSE */

    RCC_EnableLsi(DISABLE); //!< LSI is turned off here to ensure that only one clock is turned on

    #if (_TEST_LSE_BYPASS_)
    RCC_ConfigLse(RCC_LSE_BYPASS);
    #else
    RCC_ConfigLse(RCC_LSE_ENABLE);
    #endif

    /* Waite LSE Ready... */
    lse_ready_count = 0;

    while ((RCC_GetFlagStatus(RCC_FLAG_LSERD) == RESET) && (lse_ready_count < RTC_LSE_TRY_COUNT)) {
        RTC_Delay_Xms(10);

        /* LSE Ready failed or timeout... */
        if (++lse_ready_count >= RTC_LSE_TRY_COUNT) {
            log_info("\r\n#>-< RTC_ClkSrc set LSE Failed!\r\n");
            return ERROR;
        }
    }

    /* Enable the LSE OSC32_IN PC14 */
    RCC_ConfigRtcClk(RCC_RTCCLK_SRC_LSE);

    SynchPrediv  = 0xFF; //!< 32.768KHz
    AsynchPrediv = 0x7F; //!< value range: 0-7F

    #elif (RTC_CLK_SRC_TYPE_LSI == CLK_SRC_TYPE)

    log_info("\r\n#o.0 RTC_ClkSrc: LSI\r\n");

    /** @note 📌系统需提前初始化LSI */
    /* Enable the LSI OSC */
    RCC_EnableLsi(ENABLE);

    while (RCC_GetFlagStatus(RCC_FLAG_LSIRD) == RESET) {
        ;
    }

    /* Enable RTC source clock LSI */
    RCC_ConfigRtcClk(RCC_RTCCLK_SRC_LSI);

    SynchPrediv  = 0x136; //!< 39.64928KHz
    AsynchPrediv = 0x7F;  //!< value range: 0-7F

    #else

#error "RTC_ClkSrc Value is error!"

    #endif

    /* Enable the RTC Clock */
    RCC_EnableRtcClk(ENABLE);
    RTC_WaitForSynchro();

    /* Configure the RTC prescaler */
    prtc->RTC_InitParam.RTC_SynchPrediv  = SynchPrediv; //!< 8M/128 = 62.5KHz
    prtc->RTC_InitParam.RTC_AsynchPrediv = AsynchPrediv;  //!< value range: 0-7F

    return SUCCESS;
}

/**
 * @brief RTC Initialization Function
 * @param prtc
 * @retval int
 */
ErrorStatus RTC_Configuration(RTC_DriverTypeDef *prtc)
{
    ISS_ASSERT(prtc != NULL);

    /* RTC clock source select & Configure RTC prescaler */
    if (SUCCESS == RTC_CLKSourceConfig(prtc)) {

        /* Configure the RTC data register */
        prtc->RTC_InitParam.RTC_HourFormat = RTC_24HOUR_FORMAT;

        /* Check on RTC init */
        if (RTC_Init(&prtc->RTC_InitParam) == ERROR) {
            log_info("\r\n#>-< RTC Prescaler Config failed\r\n");
        }

        /* RTC Date time and Alarm value set */
        if (prtc->p_DateParam != NULL) {
            RTC_DateRegulate(prtc->p_DateParam);
        }

        if (prtc->p_TimeParam != NULL) {
            RTC_TimeRegulate(prtc->p_TimeParam);
        }

        if (prtc->p_A_AlarmParam != NULL) {
            RTC_AlarmRegulate(RTC_A_ALARM, prtc->p_A_AlarmParam);
        }

        if (prtc->p_B_AlarmParam != NULL) {
            RTC_AlarmRegulate(RTC_B_ALARM, prtc->p_B_AlarmParam);
        }

        log_info("\r\n#>o< RTC Init Success\r\n\r\n");
        return SUCCESS;
    }

    log_info("\r\n#>-< RTC Init Failed\r\n\r\n");
    return ERROR;
}

/**
 * @brief RTC alarm config EXTI Interrupt.
 * @param Cmd Interrupt enable or disable
 */
void EXTI17_RTCAlarm_Configuration(FunctionalState Cmd)
{
    EXTI_InitType EXTI_InitStructure;
    NVIC_InitType NVIC_InitStructure;

    EXTI_ClrITPendBit(EXTI_LINE17);

    EXTI_InitStructure.EXTI_Line = EXTI_LINE17;
    #ifdef __TEST_SEVONPEND_WFE_NVIC_DIS__
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event;
    #else
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    #endif
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_InitPeripheral(&EXTI_InitStructure);

    /* Enable the RTC Alarm Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel                   = RTCAlarm_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = Cmd;
    NVIC_Init(&NVIC_InitStructure);
}

/**
 * @brief Config RTC wake up Interrupt.
 * @param Cmd Interrupt enable or disable
 */
void EXTI20_RTCWKUP_Configuration(FunctionalState Cmd)
{
    EXTI_InitType EXTI_InitStructure;
    NVIC_InitType NVIC_InitStructure;

    EXTI_ClrITPendBit(EXTI_LINE20);

    EXTI_InitStructure.EXTI_Line = EXTI_LINE20;
    #ifdef __TEST_SEVONPEND_WFE_NVIC_DIS__
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event;
    #else
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    #endif
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_InitPeripheral(&EXTI_InitStructure);

    /* Enable the RTC WakeUp Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel                   = RTC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = Cmd;
    NVIC_Init(&NVIC_InitStructure);
}

/* END OF FILE ---------------------------------------------------------------*/
